<?php
// $Id: install.inc 27 2010-11-16 12:49:54Z mjohnson60@gmail.com $

/**
 * User: Titan
 * Date: Sep 28, 2010
 * Time: 6:41:22 PM
 */

Loader::import('Installer');

/**
 * @var int Enter description here ...
 */
define('SCHEMA_UNINSTALLED', -1);
define('SCHEMA_INSTALLED', 0);

/**
 * @var int Enter description here ...
 */
define('REQUIREMENT_INFO', -1);
define('REQUIREMENT_OK', 0);
define('REQUIREMENT_WARNING', 1);
define('REQUIREMENT_ERROR', 2);

/**
 * @var int Enter description here ...
 */
define('FILE_EXIST',          1);
define('FILE_READABLE',       2);
define('FILE_WRITABLE',       4);
define('FILE_EXECUTABLE',     8);
define('FILE_NOT_EXIST',      16);
define('FILE_NOT_READABLE',   32);
define('FILE_NOT_WRITABLE',   64);
define('FILE_NOT_EXECUTABLE', 128);

/**
 * install_launch()
 * 
 * Send the user to the installer page
 * 
 * @param mixed $path
 * @return void
 */
function install_launch($path)
{
	header('Location: ' . Config::baseUrl() . '/' . $path);
	header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
	header('Cache-Control: no-store, no-cache, must-revalidate');
	header('Cache-Control: post-check=0, pre-check=0', FALSE);
	header('Pragma: no-cache');
	exit();
}

/**
 * Verify the state of the specified file.
 *
 * @param $file
 * The file to check for.
 * @param $mask
 * An optional bitmask created from various FILE_* constants.
 * @param $type
 * The type of file. Can be file (default), dir, or link.
 * @return
 * TRUE on success or FALSE on failure. A message is set for the latter.
 */
function verify_install_file($file, $mask = NULL, $type = 'file')
{
	$return = TRUE;
	// Check for files that shouldn't be there.
	if (isset($mask) && ($mask & FILE_NOT_EXIST) && file_exists($file))
	{
		return FALSE;
	}
	// Verify that the file is the type of file it is supposed to be.
	if (isset($type) && file_exists($file))
	{
		$check = 'is_' . $type;
		if (!function_exists($check) || !$check($file))
		{
			$return = FALSE;
		}
	}
	
	// Verify file permissions.
	if (isset($mask))
	{
		$masks = array(
			FILE_EXIST, 
			FILE_READABLE, 
			FILE_WRITABLE, 
			FILE_EXECUTABLE, 
			FILE_NOT_READABLE, 
			FILE_NOT_WRITABLE, 
			FILE_NOT_EXECUTABLE
		);
		
		foreach ($masks as $current_mask)
		{
			if ($mask & $current_mask)
			{
				switch ($current_mask)
				{
					case FILE_EXIST:
						if (!file_exists($file))
						{
							if ($type == 'dir')
							{
								install_mkdir($file, $mask);
							}
							
							if (!file_exists($file))
							{
								$return = FALSE;
							}
						}
						
						break;
					case FILE_READABLE:
						if (!is_readable($file) && !install_fix_file($file, $mask))
						{
							$return = FALSE;
						}
						
						break;
					case FILE_WRITABLE:
						if (!is_writable($file) && !install_fix_file($file, $mask))
						{
							$return = FALSE;
						}
						
						break;
					case FILE_EXECUTABLE:
						if (!is_executable($file) && !install_fix_file($file, $mask))
						{
							$return = FALSE;
						}
						
						break;
					case FILE_NOT_READABLE:
						if (is_readable($file) && !install_fix_file($file, $mask))
						{
							$return = FALSE;
						}
						
						break;
					case FILE_NOT_WRITABLE:
						if (is_writable($file) && !install_fix_file($file, $mask))
						{
							$return = FALSE;
						}
						
						break;
					case FILE_NOT_EXECUTABLE:
						if (is_executable($file) && !install_fix_file($file, $mask))
						{
							$return = FALSE;
						}
						
						break;
				}
			}
		}
	}
	
	return $return;
}

/**
 * Attempt to fix file permissions.
 *
 * The general approach here is that, because we do not know the security
 * setup of the webserver, we apply our permission changes to all three
 * digits of the file permission (i.e. user, group and all).
 *
 * To ensure that the values behave as expected (and numbers don't carry
 * from one digit to the next) we do the calculation on the octal value
 * using bitwise operations. This lets us remove, for example, 0222 from
 * 0700 and get the correct value of 0500.
 *
 * @param $file
 *  The name of the file with permissions to fix.
 * @param $mask
 *  The desired permissions for the file.
 * @param $message
 *  (optional) Whether to output messages. Defaults to TRUE.
 *
 * @return
 *  TRUE/FALSE whether or not we were able to fix the file's permissions.
 */
function install_fix_file($file, $mask, $message = TRUE)
{
	$mod = fileperms($file) & 0777;
	$masks = array(
		FILE_READABLE, 
		FILE_WRITABLE, 
		FILE_EXECUTABLE, 
		FILE_NOT_READABLE, 
		FILE_NOT_WRITABLE, 
		FILE_NOT_EXECUTABLE
	);
	
	// FILE_READABLE, FILE_WRITABLE, and FILE_EXECUTABLE permission strings
	// can theoretically be 0400, 0200, and 0100 respectively, but to be safe
	// we set all three access types in case the administrator intends to
	// change the owner of settings.php after installation.
	foreach ($masks as $m)
	{
		if ($mask & $m)
		{
			switch ($m)
			{
				case FILE_READABLE:
					if (!is_readable($file))
					{
						$mod |= 0444;
					}
					
					break;
				case FILE_WRITABLE:
					if (!is_writable($file))
					{
						$mod |= 0222;
					}
					
					break;
				case FILE_EXECUTABLE:
					if (!is_executable($file))
					{
						$mod |= 0111;
					}
					
					break;
				case FILE_NOT_READABLE:
					if (is_readable($file))
					{
						$mod &= ~0444;
					}
					
					break;
				case FILE_NOT_WRITABLE:
					if (is_writable($file))
					{
						$mod &= ~0222;
					}
					
					break;
				case FILE_NOT_EXECUTABLE:
					if (is_executable($file))
					{
						$mod &= ~0111;
					}
					
					break;
			}
		}
	}
	
	// chmod() will work if the web server is running as owner of the file.
	// If PHP safe_mode is enabled the currently executing script must also
	// have the same owner.
	if (@chmod($file, $mod))
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

/**
 * Create a directory with specified permissions.
 *
 * @param file
 *  The name of the directory to create;
 * @param mask
 *  The permissions of the directory to create.
 * @param $message
 *  (optional) Whether to output messages. Defaults to TRUE.
 *
 * @return
 *  TRUE/FALSE whether or not the directory was successfully created.
 */
function install_mkdir($file, $mask, $message = TRUE)
{
	$mod = 0;
	$masks = array(
		FILE_READABLE, 
		FILE_WRITABLE, 
		FILE_EXECUTABLE, 
		FILE_NOT_READABLE, 
		FILE_NOT_WRITABLE, 
		FILE_NOT_EXECUTABLE
	);
	
	foreach ($masks as $m)
	{
		if ($mask & $m)
		{
			switch ($m)
			{
				case FILE_READABLE:
					$mod += 444;
					break;
				case FILE_WRITABLE:
					$mod += 222;
					break;
				case FILE_EXECUTABLE:
					$mod += 111;
					break;
			}
		}
	}
	
	if (@mkdir($file, intval("0$mod", 8)))
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

/**
 * install_write_settings()
 * 
 * Writes the settings to a settings.inc file
 * 
 * @param mixed $settings
 * @return void
 */
function install_write_settings($settings = array())
{
	$keys = array();
	foreach ($settings as $setting => $data)
	{
		$GLOBALS[$setting] = $data;
		$keys[] = $setting;
	}
	
	$buffer = NULL;
	$first = TRUE;
	if (($fp = fopen('./default.settings.inc', 'r')) !== FALSE)
	{
		while (!feof($fp))
		{
			$line = fgets($fp);
			if ($first && substr($line, 0, 5) != '<?php')
			{
				$buffer = "<?php\n\n";
			}
			$first = FALSE;
			
			if (substr($line, 0, 1) == '$')
			{
				preg_match('/\$([^ ]*) /', $line, $variable);
				if (in_array($variable[1], $keys))
				{
					// this becomes $setting = 'value';
					$setting = $settings[$variable[1]];
					$buffer .= '$' . $variable[1] . " = '" . $setting . "';\n";
					unset($settings[$variable[1]]);
				}
				else
				{
					// comment or explanation 
					$buffer .= $line;
				}
			}
			else
			{
				$buffer .= $line;
			}
		}
		
		fclose($fp);
		
		$fp = @fopen('./settings.inc', 'w');
		if (!$fp || fwrite($fp, $buffer) === FALSE)
		{
			Athena::setMessage('Failed to write to the settings.inc file. Please verify the file permissions.');
			
			return FALSE;
		}
		
		return TRUE;
	}
	else
	{
		Athena::setMessage('Failed to open the default.settings.inc file. Please verify the file permissions.');
		
		return FALSE;
	}
}

/**
 * install_schema()
 * 
 * Installs the database schema
 * 
 * @return void
 */
function install_schema()
{
	$schema = Db::schema();
	if (isset($schema) && $schema !== FALSE)
	{
		Db::query("DROP DATABASE IF EXISTS %s", $schema);
		$result = Db::query("CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci", $schema);
		
		if ($result !== FALSE)
		{
			mysql_select_db($schema);
			Athena::setMessage('The <code>' . $schema . '</code> database was successfully created.');
			
			return TRUE;
		}
		else
		{
			Athena::setMessage('The <code>' . $schema . '</code> database was not created.');
			
			return FALSE;
		}
	}
	
	return FALSE;
}

/**
 * install_system()
 * 
 * Install all the databases that the system consists of
 * 
 * @return void
 */
function install_system()
{
	$result = install_schema();
	
	$module_path = dirname(Athena::getFilename('module', 'system'));
	require_once('./' . $module_path . '/system.install');
	
	$installer = new SystemInstaller(MODULE_LOAD_INSTALL);
	$installer->install();
	
	Db::query("INSERT INTO system (filename, name, type, owner, status, initialize, version, weight) VALUES('%s', '%s', '%s', '%s', %d, %d, %d, %d)", $module_path . '/system.module', 'system', 'module', '', 1, $installer->initialize, $installer->schema_version, $installer->weight);
	Initializer::initialize(INITIALIZE_FULL);
	
	Moduler::rebuildCache();
	
	return $result;
}
